﻿#region Copyright(C) 2018 FI Ltd. All rights reserved.

// ==================================================================================================
// Copyright(C) 2018 FI Ltd. All rights reserved.
//
// SYSTEM NAME	:   FI.Net Application Framework 
// COMPONENT ID :   FI.EntityFramework
// COMPONENT DESC:  
//
// CREATED DATE/BY: 18 Aug 2018 / Tian Mi
//
// REVISION HISTORY:	DATE/BY			SR#/CS/PM#/OTHERS		DESCRIPTION OF CHANGE
// ==================================================================================================

#endregion

using System;
using System.Collections.ObjectModel;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace FI.EntityFramework.Interception
{
    /// <summary>
    /// A helper for validating and rebuilding the <see cref="Oracle.ManagedDataAccess.Client.OracleCommand"/>.
    /// </summary>
    internal class OracleCommandHelper : DbCommandHelper
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="OracleCommandHelper"/> class.
        /// </summary>
        /// <param name="command">The command.</param>
        internal OracleCommandHelper(DbCommand command)
            : base(command)
        {
        }

        /// <summary>
        /// Gets the delete statement from the current command.
        /// </summary>
        /// <returns>The delete statement object.</returns>
        protected override DeleteCommandStatement GetDeleteCommandStatement()
        {
            var targetMatch = Regex.Match(this.Command.CommandText, "^DELETE \"(?<schema>\\S+?)\"\\.\"(?<table>\\S+?)\"", RegexOptions.IgnoreCase);
            var conditionMatches = Regex.Matches(this.Command.CommandText, "\\(\"(?<column>\\S+?)\" = (?<parameter>\\S+?)\\)", RegexOptions.IgnoreCase);

            var schema = targetMatch.Groups["schema"].Value;
            var table = targetMatch.Groups["table"].Value;
            var condition = conditionMatches.Cast<Match>()
                .Select((m) => new CommandClause(m.Groups["column"].Value, m.Groups["parameter"].Value))
                .ToList();
            return new DeleteCommandStatement(schema, table, condition);
        }

        /// <summary>
        /// Formats parameter name for the current command.
        /// </summary>
        /// <param name="arg">The given argument i.e. property name or numeric.</param>
        /// <returns>
        /// The parameter name for the current command.
        /// </returns>
        protected override string GetParameterName(object arg)
        {
            return String.Format(":p{0}", arg);
        }

        /// <summary>
        /// Re-generate the delete command text for the current command.
        /// </summary>
        /// <param name="statement">The delete statement object.</param>
        /// <returns>
        /// The command text for the current command.
        /// </returns>
        protected override string GenerateDeleteCommandText(DeleteCommandStatement statement)
        {
            var updateContent = statement.UpdateContent.Select((c) => String.Format("\"{0}\" = {1}", c.Column, c.Parameter));
            var updateCondition = statement.UpdateCondition.Select((c) => String.Format("(\"{0}\" = {1})", c.Column, c.Parameter));
            var deleteCondition = statement.DeleteCondition.Select((c) => String.Format("(\"{0}\" = {1})", c.Column, c.Parameter));

            var builder = new StringBuilder();
            builder.Append("BEGIN ");
            builder.AppendFormat("UPDATE \"{0}\".\"{1}\"", statement.Schema, statement.Table);
            builder.AppendFormat(" SET {0}", String.Join(", ", updateContent));
            builder.AppendFormat(" WHERE {0};", String.Join(" AND ", updateCondition));
            builder.AppendFormat("DELETE \"{0}\".\"{1}\"", statement.Schema, statement.Table);
            builder.AppendFormat(" WHERE {0};", String.Join(" AND ", deleteCondition));
            builder.Append(" END;");
            return builder.ToString();
        }

        /// <summary>
        /// Validates the current command if it's a delete command.
        /// </summary>
        /// <returns>
        /// if set to <c>true</c> the current command is delete command.
        /// </returns>
        public override bool ValidateDeleteCommand()
        {
            var match = Regex.Match(this.Command.CommandText, "^DELETE \"\\S+\"\\.\"\\S+\"", RegexOptions.IgnoreCase);
            return match.Success;
        }
    }
}